package top.cardone.context;

import lombok.Setter;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.util.MethodInvoker;

import java.lang.reflect.InvocationTargetException;

/**
 * Created by cardo on 2018/2/10 0010.
 */
@Log4j2
public class TaskExecutorMethodInvoker {
    @Setter
    protected String taskExecutorBeanName;

    @Setter
    private Class<?> targetType;

    @Setter
    private Object targetObject;

    @Setter
    private String targetMethod;

    @Setter
    private String staticMethod;

    @Setter
    private int limtNum = 0;

    private int runCount = 0;

    public Object taskExecutorInvoke(Object[] arguments) {
        if (StringUtils.isEmpty(taskExecutorBeanName)) {
            if (limtNum == 0) {
                return this.invoke(arguments);
            }

            if (runCount > limtNum) {
                return null;
            }

            runCount++;

            if (runCount > limtNum) {
                return null;
            }

            Object r = null;

            while (runCount > 0) {
                try {
                    r = this.invoke(arguments);

                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    log.error(e);
                } finally {
                    runCount--;
                }
            }

            return r;
        } else {
            ApplicationContextHolder.getBean(AsyncListenableTaskExecutor.class, this.taskExecutorBeanName).submitListenable(() -> {
                this.invoke(arguments);
            });
        }

        return null;
    }

    private Object invoke(Object[] arguments) {
        try {
            MethodInvoker methodInvoker = new MethodInvoker();

            if (targetObject == null) {
                if (this.targetType != null) {
                    methodInvoker.setTargetObject(ApplicationContextHolder.getBean(this.targetType));
                }
            } else {
                methodInvoker.setTargetObject(this.targetObject);
            }

            methodInvoker.setStaticMethod(this.staticMethod);
            methodInvoker.setTargetMethod(this.targetMethod);
            methodInvoker.setArguments(arguments);

            methodInvoker.prepare();

            return methodInvoker.invoke();
        } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
}
